package interfaces.tables;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.fenggui.ILabel;
import org.fenggui.IWidget;
import org.fenggui.ScrollBar;
import org.fenggui.ScrollContainer;
import org.fenggui.StandardWidget;
import org.fenggui.appearance.LabelAppearance;
import org.fenggui.binding.render.ImageFont;
import org.fenggui.decorator.background.PlainBackground;
import org.fenggui.decorator.border.PlainBorder;
import org.fenggui.event.mouse.IMouseListener;
import org.fenggui.event.mouse.MouseClickedEvent;
import org.fenggui.event.mouse.MouseDoubleClickedEvent;
import org.fenggui.event.mouse.MouseDraggedEvent;
import org.fenggui.event.mouse.MouseEnteredEvent;
import org.fenggui.event.mouse.MouseExitedEvent;
import org.fenggui.event.mouse.MouseMovedEvent;
import org.fenggui.event.mouse.MousePressedEvent;
import org.fenggui.event.mouse.MouseReleasedEvent;
import org.fenggui.event.mouse.MouseWheelEvent;
import org.fenggui.util.Color;

import com.jme.system.DisplaySystem;

import fileHandling.language.Lang;

import interfaces.GUISource;
import interfaces.language.LanguageLabel;
import interfaces.superWidgets.StaticContent;

public abstract class Table<T extends TableEntry> extends StaticContent {
	
	protected List<T> tableEntries;
	protected Lang[] columnNames;
	protected float[] columnWidths;
	
	protected int border, labelHeight, headerHeight, tableContentHeight, entryWidth;
	protected StaticContent tableContent;
	protected ScrollContainer scrollContent;
	
	protected T selectedEntry;
	protected HashMap<T, IMouseListener> mouseListeners;
	
	public Table(int width, int height, int x, int y, Lang[] columnNames, float[] columnWidths) {
		super(width, height, x, y);
		this.columnNames = columnNames;
		this.columnWidths = columnWidths;
		assert(columnNames.length == columnWidths.length);
		
		getAppearance().add(new PlainBackground(getTableBGColor()));
		
		mouseListeners = new HashMap<T, IMouseListener>();
		
		tableEntries = new ArrayList<T>();
		
		border = getBorder();
		labelHeight = getLabelHeight();
		headerHeight = getHeaderHeight();
		
		initTableContent();
		initHeader();
		
		layout();
	}
	
	public int getLabelHeight() { return DisplaySystem.getDisplaySystem().getHeight() / 25; }
	
	protected int getHeaderHeight() { return DisplaySystem.getDisplaySystem().getHeight() / 25; }
	
	protected int getBorder() { return height / 20;}
	
	protected void initTableContent() {
		int tableWidth = width - (2 * border);
		tableContentHeight = height - headerHeight - border;
		
		scrollContent = GUISource.createScrollContainer();
		scrollContent.setXY(border, border);
		scrollContent.setMinSize(tableWidth, tableContentHeight);
		scrollContent.setSizeToMinSize();
		scrollContent.setShowScrollbars(showScrollBars());
		
		ScrollBar horScrollBar = scrollContent.getHorizontalScrollBar();
		ScrollBar verScrollBar = scrollContent.getVerticalScrollBar();
		
		addWidget(scrollContent);
		layout();
		
		horScrollBar.setMinSize(0, 0);
		horScrollBar.setSizeToMinSize();
		horScrollBar.setVisible(false);
		horScrollBar.setEnabled(false);
		
		if(showScrollBars()) {
			LabelAppearance buttonApp = verScrollBar.getSlider().getSliderButton().getAppearance();
			buttonApp.add(new PlainBorder(getSliderButtonBorderColor()));
			buttonApp.add(new PlainBackground(getSliderButtonBGColor()));
		}
		
		entryWidth = width;
		
		tableContent = new StaticContent(entryWidth, labelHeight, 0, tableContentHeight - labelHeight);
		tableContent.setMinSize(tableContent.getSize());
		scrollContent.setInnerWidget(tableContent);
	}
	
	protected boolean showScrollBars() { return true; }
	
	protected void initHeader() {
		int labelY = 0;
		int labelX = border;
		int labelWidth = (width - (2 * border));
		
		int contY = getHeight() - headerHeight;
		StaticContent labelContent = new StaticContent(width, headerHeight, 0, contY);
		labelContent.getAppearance().add(new PlainBackground(getHeaderBGColor()));
		addWidget(labelContent);
		
		for(int i = 0; i < columnNames.length; i++) {
			Lang columnName = columnNames[i];
			int nextWidth = (int)(labelWidth * columnWidths[i]);
			
			StandardWidget headWidget = getNextHeadWidget(columnName);
			headWidget.setXY(labelX, labelY);
			headWidget.setSize(nextWidth, headerHeight);
			if(headWidget instanceof ILabel) {
				ILabel label = (ILabel)headWidget;
				GUISource.setTextColor(label, getHeaderTextColor());
				GUISource.setFont(label, getHeaderFont());
			}
			labelContent.addWidget(headWidget);
			
			labelX += nextWidth;
		}
	}
	
	protected StandardWidget getNextHeadWidget(Lang lang) {
		return new LanguageLabel(lang, getHeaderFont());
	}
	
	protected abstract Color getHeaderTextColor();
	
	protected abstract Color getHeaderBGColor();
	
	protected abstract Color getTableBGColor();
	
	public abstract boolean allowSelection();
	
	public void addTableEntry(T entry) {
		tableEntries.add(entry);
		tableContent.addWidget(entry);
		updateTable();
		
		if(allowSelection()) {
			IMouseListener l = getTableMouseListener();
			mouseListeners.put(entry, l);
			entry.addMouseListener(l);
		}
		layout();
	}
	
	@SuppressWarnings("unchecked")
	protected void mouseClicked(IWidget entry) {
		changeSelection((T)entry);
	}
	
	protected void changeSelection(T newSelection) {
		if(selectedEntry != null) selectedEntry.setSelected(false);
		
		selectedEntry = newSelection;
		selectedEntry.setSelected(true);
	}
	
	@SuppressWarnings("unchecked")
	protected void mouseDoubleClicked(IWidget entry) {
		changeSelection((T)entry);
	}
	
	protected IMouseListener getTableMouseListener() {
		return new IMouseListener() {
			@Override
			public void mouseDoubleClicked(MouseDoubleClickedEvent mdce) {
				Table.this.mouseDoubleClicked(mdce.getSource().getParent());
			}
			
			@Override
			public void mouseClicked(MouseClickedEvent mce) {
				Table.this.mouseClicked(mce.getSource().getParent());
			}
			
			@Override
			public void mouseWheel(MouseWheelEvent mouseWheelEvent) {}
			@Override
			public void mouseReleased(MouseReleasedEvent mouseReleasedEvent) {}
			@Override
			public void mousePressed(MousePressedEvent mousePressedEvent) {}
			@Override
			public void mouseMoved(MouseMovedEvent mouseMovedEvent) {}
			@Override
			public void mouseExited(MouseExitedEvent mouseExited) {}
			@Override
			public void mouseEntered(MouseEnteredEvent mouseEnteredEvent) {}
			@Override
			public void mouseDragged(MouseDraggedEvent mouseDraggedEvent) {}
		};
	}
	
	public void removeTableEntry(T entry) {
		tableEntries.remove(entry);
		tableContent.removeWidget(entry);
		updateTable();
		
		if(allowSelection()) {
			IMouseListener l = mouseListeners.remove(entry);
			assert(l != null);
			entry.removeMouseListener(l);
		}
		layout();
	}
	
	protected void updateTable() {
		int newHeight = tableEntries.size() * labelHeight;
		if(newHeight < tableContentHeight) newHeight = tableContentHeight;
		tableContent.setMinSize(entryWidth, newHeight);
		tableContent.setSizeToMinSize();
		tableContent.setY(0);
		
		for(int i = 0; i < tableEntries.size(); i++) {
			T entry = tableEntries.get(i);
			entry.setY(newHeight - ((i + 1) * labelHeight));
		}
				
		layout();
	}
	
	public Lang[] getColumnNames() { return columnNames; }
	
	public float[] getColumnWidths() { return columnWidths; }
	
	public ImageFont getLabelFont() { return GUISource.middleFont; }
	
	public ImageFont getHeaderFont() { return GUISource.middleFont; }
	
	public Color getSliderButtonBorderColor() { return Color.BLACK; }
	
	public Color getSliderButtonBGColor() { return Color.DARK_GRAY; }
	
	public void clearTableEntries() {
		List<T> entries = new ArrayList<T>(tableEntries);
		for(T entry : entries) {
			removeTableEntry(entry);
		}
		entries.clear();
		updateTable();
	}
	
	public List<T> getTableEntries() { return tableEntries; }
	
	public ScrollContainer getScrollContent() { return scrollContent; }
	
	public StaticContent getTableContent() { return tableContent; }
}